﻿namespace Authentication.Web.Controller
{
    using Authentication.Configuration;
    using Authentication.Model.Interface;
    using Authentication.Provider;
    using Common.Cache;
    using Common.Contract;
    using Common.Contract.Authentication;
    using Common.Contract.Authentication.Record;
    using Common.Contract.Authentication.Request;
    using Common.Service;
    using Common.Util;
    using Common.Web.Share;
    using System;
    using System.Linq;
    using System.Collections.Generic;
    using System.Web.Mvc;
    using Common.Type;
    using Common.Contract.SystemConfig;
    using Common.Contract.SystemConfig.Request;
    using Common.Contract.SystemConfig.Record;
    using Authentication.Model.ViewModel;

    public class ControllerBase : System.Web.Mvc.Controller
    {
        public ControllerBase()
        {
            if (this.Provider == null) this.Provider = new ProviderFactory();
        }
        protected List<string> ALLOW_PAGES = new List<string>() { "/HOME/ERROR/", "/HOME/DATAFIX/", "/ACCOUNT/LOGOUT/" };

        public IProviderFactory Provider { get; private set; }

        public Guid ApplicationKey { get { return SystemConfiguration.ApplicationKey; } }

        public string AccessRoleIdentifier
        {
            get
            {
                return CacheFactory.MemCache.GetFromCache<string>(CACHING_NAME.ROLE_ACCESS_IDENTIFIER, () =>
                {
                    var header = new RequestHeaderRecord
                    {
                        ApplicationKey = this.ApplicationKey,
                        CallerName = "Authentication.Web.Controller.ControllerBase.AccessRoleIdentifier"
                    };

                    using (ServiceClient<IConfiguration> serviceClient = new ServiceClient<IConfiguration>())
                    {
                        RetrieveDictionaryRequest request = new RetrieveDictionaryRequest
                        {
                            Header = header,
                            Key = SystemConfiguration.RoleLinkAccessKey
                        };
                        var objectResponse = serviceClient.Proxy.RetrieveDictionary(request);


                        if (objectResponse.IsSuccess)
                        {
                            return objectResponse.Record.Value;
                        }
                        else
                        {
                            DictionaryRecord record = new DictionaryRecord { ApplicationKey = SystemConfiguration.ApplicationKey, Key = SystemConfiguration.RoleLinkAccessKey, Value = SystemConfiguration.RoleLinkAccess };
                            AddDictionaryRequest request3 = new AddDictionaryRequest
                            {
                                Header = header,
                                Record = record
                            };
                            var response = serviceClient.Proxy.AddDictionary(request3);
                        }
                    }
                    return SystemConfiguration.RoleLinkAccess;
                }, SystemConfiguration.DefaultCacheTime);
            }
        }

        public string CurrentUser
        {
            get { return Membership.CurrentUser; }
        }

        public string CurrentURL
        {
            get
            {
                var urlLocal = Request.Url.LocalPath.ToLower();
                if (!string.IsNullOrEmpty(urlLocal) && !urlLocal.EndsWith("/")) urlLocal = urlLocal.ToLower() + "/";
                else if (urlLocal == "/") urlLocal = string.Empty;
                return urlLocal;
            }
        }

        public List<UserRoleRecord> CurrentObjectRoles
        {
            get
            {
                var urlLocal = Request.Url.OriginalString.ToLower();
                var menuAllow = this.CurrentRules.Where(x => x.Value.Object.ObjectUrl != null &&
                                                             x.Value.Object.ObjectUrl.Length > 0 &&
                                                             urlLocal.Contains(x.Value.Object.ObjectUrl.ToLower())).ToList();
                var objects = menuAllow.Select(x => x.Value).ToList();
                return objects;
            }
        }

        public UserRecord CurrentUserRecord
        {
            get
            {
                if (HttpContext.Items[HTTP_CONTEXT.CURRENT_USER_RECORD] == null)
                {
                    using (ServiceClient<IAuthentication> serviceClient = new ServiceClient<IAuthentication>())
                    {
                        RetrieveUserByUserNameRequest request = new RetrieveUserByUserNameRequest
                        {
                            Header = new RequestHeaderRecord
                            {
                                ApplicationKey = this.ApplicationKey,
                                CallerName = "Authentication.Web.Controller.ControllerBase.CurrentUserRecord"
                            },
                            UserName = CurrentUser
                        };
                        var objectResponse = serviceClient.Proxy.RetrieveUserByUserName(request);
                        HttpContext.Items[HTTP_CONTEXT.CURRENT_USER_RECORD] = objectResponse.Record;
                    }

                }
                return HttpContext.Items[HTTP_CONTEXT.CURRENT_USER_RECORD] as UserRecord;
            }
        }

        public Dictionary<string, UserRoleRecord> CurrentRules
        {
            get
            {
                if (CurrentUser.IsNullOrWhiteSpace()) return null;
                string groupId = CurrentUserRecord.GroupIdentifier;
                string userId = CurrentUserRecord.UserName;
                Dictionary<string, UserRoleRecord> result = new Dictionary<string, UserRoleRecord>();
                return CacheFactory.MemCache.GetFromCache<Dictionary<string, UserRoleRecord>>(string.Format("[{0}].[{1}]", CACHING_NAME.CURRENT_USER_RECORD_RULES_DICT, userId), () =>
                {
                    List<ObjectRecord> menuitems = null;
                    Dictionary<string, UserRoleRecord> userDicts = new Dictionary<string, UserRoleRecord>();
                    Dictionary<string, GroupRoleRecord> groupDicts = new Dictionary<string, GroupRoleRecord>();

                    using (ServiceClient<IAuthentication> serviceClient = new ServiceClient<IAuthentication>())
                    {
                        RetrieveObjectsRequest request = new RetrieveObjectsRequest
                        {
                            Header = new RequestHeaderRecord
                            {
                                ApplicationKey = this.ApplicationKey,
                                CallerName = "Authentication.Web.Controller.ControllerBase.CurrentRules"
                            }
                        };
                        var objectResponse = serviceClient.Proxy.RetrieveObjects(request);
                        if (objectResponse.IsSuccess)
                        {
                            menuitems = objectResponse.Records as List<ObjectRecord>;
                        }

                        if (menuitems != null)
                        {
                            menuitems = menuitems.OrderBy(x => x.ObjectIdentifier)/*OrderBy(x => x.OLEVEL).ThenBy(y => y.ORDER_ID).ThenBy(z => z.ID)*/
                                                 .RecursivelyData("ObjectIdentifier", "ParentIdentifier", "ObjectName").ToList();
                        }


                        if (userId != SystemConfiguration.SUPPER_ADMIN)
                        {
                            // Group rule
                            RetrieveGroupRolesRequest requestGroupRule = new RetrieveGroupRolesRequest
                            {
                                Header = new RequestHeaderRecord
                                {
                                    ApplicationKey = this.ApplicationKey,
                                    CallerName = "Authentication.Web.Controller.ControllerBase.CurrentRules"
                                },
                                GroupIdentifier = groupId
                            };
                            var groupRulesResponse = serviceClient.Proxy.RetrieveGroupRoles(requestGroupRule);
                            if (groupRulesResponse.IsSuccess && groupRulesResponse.Records != null)
                            {
                                foreach (GroupRoleRecord obj in groupRulesResponse.Records)
                                {
                                    var key = obj.GroupIdentifier + "$" + obj.ObjectIdentifier;
                                    if (!groupDicts.ContainsKey(key)) groupDicts.Add(key, obj);
                                }
                            }

                            // User rule
                            RetrieveUserRolesRequest requestUserRule = new RetrieveUserRolesRequest
                            {
                                Header = new RequestHeaderRecord
                                {
                                    ApplicationKey = this.ApplicationKey,
                                    CallerName = "Authentication.Web.Controller.ControllerBase.CurrentRules"
                                },
                                UserName = userId
                            };
                            var userRulesResponse = serviceClient.Proxy.RetrieveUserRoles(requestUserRule);

                            if (userRulesResponse.IsSuccess && userRulesResponse.Records != null)
                            {
                                foreach (UserRoleRecord obj in userRulesResponse.Records)
                                {
                                    var key = obj.UserName + "#" + obj.ObjectIdentifier;
                                    if (!userDicts.ContainsKey(key)) userDicts.Add(key, obj);
                                }
                            }
                        }


                        if (menuitems != null)
                        {
                            foreach (var mitem in menuitems)
                            {
                                if (userId == SystemConfiguration.SUPPER_ADMIN)
                                {
                                    UserRoleRecord ruleAll = new UserRoleRecord
                                    {
                                        ApplicationKey = mitem.ApplicationKey,
                                        UserName = userId,
                                        ObjectIdentifier = mitem.ObjectIdentifier,
                                        Object = mitem,
                                        Active = true
                                    };
                                    result.Add(mitem.ObjectIdentifier, ruleAll);
                                    continue;
                                }

                                string keyGroup = groupId + "$" + mitem.ObjectIdentifier;
                                string keyUser = userId + "#" + mitem.ObjectIdentifier;
                                if (userDicts.ContainsKey(keyUser))
                                {
                                    result.Add(mitem.ObjectIdentifier, userDicts[keyUser]);
                                }
                                else if (groupDicts.ContainsKey(keyGroup))
                                {
                                    GroupRoleRecord group = groupDicts[keyGroup];
                                    UserRoleRecord ruleGroup = new UserRoleRecord
                                    {
                                        ApplicationKey = mitem.ApplicationKey,
                                        UserName = userId,
                                        ObjectIdentifier = mitem.ObjectIdentifier,
                                        Object = mitem,
                                        RoleIdentifier = group.RoleIdentifier,
                                        Active = true
                                    };
                                    result.Add(mitem.ObjectIdentifier, ruleGroup);
                                }
                            }
                        }
                    }
                    return result;
                }
                , SystemConfiguration.DefaultCacheTime);
            }
        }

        protected List<TreeViewNode> MenuNodes
        {
            get { return GetMenuNodes(this.CurrentUser); }
        }

        #region Helper functions
        protected override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            base.OnActionExecuting(filterContext);
            if (filterContext.HttpContext.Request.HttpMethod != "POST"
                && !CurrentURL.IsNullOrWhiteSpace() && CurrentURL != "/" && !ALLOW_PAGES.Any(x => CurrentURL.ToUpper().Contains(x))
                && !CurrentUser.IsNullOrWhiteSpace() && CurrentUser != SystemConfiguration.SUPPER_ADMIN)
            {
                var url = filterContext.HttpContext.Request.RawUrl.ToLower();// ====> /admin/manage
                var menuAllow = this.CurrentRules.Where(x => !x.Value.Object.ObjectUrl.IsNullOrWhiteSpace()).Select(x => x.Value.Object.ObjectUrl.EndsWith("/") ? x.Value.Object.ObjectUrl.ToLower() : x.Value.Object.ObjectUrl.ToLower() + "/").ToList();

                var Active = menuAllow.Any(x => CurrentURL.Contains(x));
                if (!Active) HttpContext.Response.Redirect("~/");
            }
        }


        protected override void OnActionExecuted(ActionExecutedContext filterContext)
        {
            string actionName = filterContext.ActionDescriptor.ActionName;
            string controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;
            var vm = filterContext.Controller.ViewData.Model as IViewModel;
            var modelFull = string.Format("{0}.{1}", controllerName, actionName);
            if (vm != null && !vm.Message.IsNullOrWhiteSpace())
            {
                ModelState.AddModelError(modelFull, vm.Message);
            }
            base.OnActionExecuted(filterContext);
        }

        protected List<TreeViewNode> GetMenuNodes(string user)
        {
            var result = CacheFactory.MemCache.GetFromCache<List<TreeViewNode>>(string.Format(CACHING_NAME.CURRENT_USER_OBJECT, user), () =>
            {
                //Get object with the rules
                IEnumerable<ObjectRecord> objectsSub = null;
                using (ServiceClient<IAuthentication> serviceClient = new ServiceClient<IAuthentication>())
                {
                    RetrieveObjectsRequest request = new RetrieveObjectsRequest
                    {
                        Header = new RequestHeaderRecord
                        {
                            ApplicationKey = SystemConfiguration.ApplicationKey,
                            CallerName = "Authentication.Web.Controller.ControllerBase.GetMenuNodes"
                        }
                    };
                    var objectResponse = serviceClient.Proxy.RetrieveObjects(request);
                    objectsSub = objectResponse.Records;
                }

                var ruleDicts = this.CurrentRules;
                if (objectsSub != null && user != SystemConfiguration.SUPPER_ADMIN)
                    objectsSub = objectsSub.Where(x => (ruleDicts.ContainsKey(x.ObjectIdentifier) && ruleDicts[x.ObjectIdentifier].Active == true && ruleDicts[x.ObjectIdentifier].RoleIdentifier == this.AccessRoleIdentifier)).ToList();


                if (objectsSub == null) return null;
                objectsSub = objectsSub.OrderBy(x => x.ObjectIdentifier).ToList();


                //Bill to Dictionary
                Dictionary<string, TreeViewNode> dict = new Dictionary<string, TreeViewNode>();
                var nodes = new List<TreeViewNode>();
                bool access = false;//Authentication values
                foreach (var obj in objectsSub)
                {
                    if (user == SystemConfiguration.SUPPER_ADMIN ||
                        (obj.ObjectUrl != null && CurrentURL.Contains(obj.ObjectUrl.ToUpper())) ||
                        ALLOW_PAGES.Contains(CurrentURL)) access = true;

                    var node = new TreeViewNode { Name = obj.ObjectIdentifier, Value = obj.ObjectName, Url = obj.ObjectUrl, CssIcon = obj.ObjectIcon };
                    if (!dict.ContainsKey(node.Name)) dict.Add(node.Name, node);
                }
                if (string.IsNullOrEmpty(CurrentURL)) access = true;

                //Authentication on Menu system
                if (!access)
                {
                    //Redirect to home if can not access
                    HttpContext.Response.Redirect("~/");
                    return null;
                }

                /*if (!CurrentURL.ToUpper().Contains("/ACCOUNT/CHANGEPASSWORD") && sysService.CheckChangePassword(paramsv.CURRENT_USER.USER_NAME, paramsv.CURRENT_USER.PASS))
                {
                    //Redirect to home if can not access
                    HttpContext.Response.Redirect("~/account/changepassword");
                    return null;
                }*/

                //bill to the tree
                foreach (var obj in objectsSub)
                {
                    if (obj.IsSubstitution == true) continue;
                    if (string.IsNullOrEmpty(obj.ParentIdentifier) || (!dict.ContainsKey(obj.ParentIdentifier)))
                    {
                        if (dict.ContainsKey(obj.ObjectIdentifier))
                        {
                            var node = dict[obj.ObjectIdentifier];
                            nodes.Add(node);
                        }
                    }
                    else
                    {
                        var subNode = dict[obj.ParentIdentifier];
                        subNode.Nodes.Add(dict[obj.ObjectIdentifier]);
                    }
                }
                return nodes;
            });

            return result;
        }
        #endregion
    }
}
